<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>俄罗斯方框</title>
	</head>
	<style>
		* {
			padding: 0;
			margin: 0;
			box-sizing: border-box;
		}
		#map {
			margin: 100px auto;
			background: #58a;
			background-image: linear-gradient(white 1px, transparent 0),
				linear-gradient(90deg, white 1px, transparent 0),
				linear-gradient(hsla(0, 0%, 100%, 0.3) 1px, transparent 0),
				linear-gradient(90deg, hsla(0, 0%, 100%, 0.3) 1px, transparent 0);
			background-size: 20px 20px;
			position: relative;
			overflow: hidden;
		}
		span {
			position: absolute;
		}
		.span {
			background-color: blue;
			border: 1px solid #ccc;
		}
		.score {
			text-align: center;
			line-height: 45px;
		}
	</style>
	<body>
		<div class="score">得分为0</div>
		<div id="map"></div>
		<script>
			let that
			class Square {
				constructor(name, score) {
					that = this
					this.map = document.querySelector(name)
					this.scoreEl = document.querySelector(score)
					this.MAP_WIDTH = 20 // 地图宽的比例
					this.MAP_HEGHT = 20 //  地图高的比例
					this.SQUARE_SIZE = 20 //每个方块的大小
					this.squares = [
						[0, 1],
						[1, 1],
						[2, 1],
						[3, 1],
					] // 目标无 【x,y】代表每一个小方块的坐标啊
					this.bottomSquares = [] // 底部结构的数组
					this.intersectX = [] // 下落过程中的交叉 x轴 坐标集合
					this.direction = [0, 1]
					this.timer = null // 定时器
					this.tims = 200
					this.score = 0
					this.init() // 初始化游戏
				}
				init() {
					// 地图的宽高
					this.map.style.width = this.MAP_WIDTH * this.SQUARE_SIZE + 'px'
					this.map.style.height = this.MAP_HEGHT * this.SQUARE_SIZE + 'px'
					document.addEventListener('keyup', that.handelKeyup)
					this.calculateIntersectX() //计算交叉范围
					this.createdTarget()
				}
				// 绘制目标物体
				starGam() {
					const squaresStr = that.drawSquares(that.squares) // 绘制目标物
					const bottomSquaresStr = that.drawSquares(that.bottomSquares) // 绘制底部
					that.map.innerHTML = squaresStr + bottomSquaresStr // 将数据放到页面
				}
				// 创建下落物体
				createdTarget() {
					let arr = []

					let x = that.random(0, 15)
					let type = that.random(-1, 6)
					// let type = that.random(0, 0)
					let xx = that.random(x, x + 2)
					if (type == 0) {
						arr = [
							[x, -2],
							[x + 1, -2],
							[x + 2, -2],
							[x + 3, -2],
						]
					} else if (type == -1) {
						arr = [
							[x, -2],
							[x + 1, -2],
							[x + 2, -2],
							[xx, -3],
						]
					} else if (type == 1) {
						arr = [
							[x, -1],
							[x + 1, -1],
							[x + 2, -1],
							[xx, 0],
						]
					} else if (type == 2) {
						arr = [
							[x, -1],
							[x + 1, -1],
						]
					} else if (type == 3) {
						arr = [
							[x, -1],
							[x, 0],
							[x, 1],
							[x, 2],
						]
					} else if (type == 4) {
						arr = [
							[x, -1],
							[x, 0],
							[x + 1, -1],
							[x + 1, 0],
						]
					} else if (type == 5) {
						arr = [
							[x, -1],
							[x, 0],
						]
					} else if (type == 6) {
						arr = [
							[0, -1],
							[0, 0],
						]
					}

					this.squares = arr

					that.createdTimer()
				}
				createdTimer() {
					if (that.timer) clearInterval(that.timer)
					that.timer = setInterval(() => {
						that.squares.forEach((item) => {
							item[1]++
						})
						that.starGam()
						that.calculateTime()
					}, that.tims)
				}
				drawSquares(arrs) {
					let str = ''
					let arr = []
					arrs.forEach((item) => {
						//每个方块的位置
						const x = item[0] * this.SQUARE_SIZE
						const y = item[1] * this.SQUARE_SIZE
						// 开始绘制
						arr.push(
							`<span data-x-y='${[
								item[0],
								item[1],
							]}' class="span"style="width: 20px; height: 20px; left:${x}px; top:${y}px"> </span>`
						)
					})
					str = arr.join('')
					return str
				}
				handelKeyup(e) {
					const code = e.keyCode
					// 上 38    下 40    左 37    右 39
					switch (code) {
						case 37:
							that.direction = [-1, 0]
							break
						case 39:
							that.direction = [1, 0]
							break
						case 38:
							that.direction = [0, -1] // y 为 -1 改变形态
							break
						case 40:
							that.direction = [0, 1] // y 为 1 快速下落
							break
					}
					that.handelMove() // 移动
					that.calculateIntersectX() //计算交叉范围
				}
				handelMove() {
					//左右移动规则
					if (that.direction[0] === -1 || that.direction[0] === 1) {
						for (let i = 0; i < that.squares.length; i++) {
							let item = that.squares[i]
							const x = (item[0] += that.direction[0])
							const xx = [x, item[1]]
							// 右侧边界越界判断
							if (x < 0) {
								that.squares.forEach((item) => {
									item[0]++
								})
							} else if (x > that.MAP_WIDTH - 1) {
								that.squares.forEach((item) => {
									item[0]--
								})
							} else {
								that.squares.splice(i, 1, xx)
							}
						}
					} else if (that.direction[1] === -1) {
						// alert('改变形态')
						that.squares = that.blokTurnRight(that.squares)

						// 不同的形状对应的旋转规则不同
					} else if (that.direction[1] == 1) {
						// 快速下落 x不变  y--
						that.squares.forEach((item, index) => {
							const newXY = [item[0], (item[1] += that.direction[1])]
							that.squares.splice(index, 1, newXY)
						})
					}
					that.calculateTime()
				}
				// 计算 清除定时器时间
				calculateTime() {
					that.stopMove()
				}
				// 计算交叉范围
				calculateIntersectX() {
					let arr = []
					that.bottomSquares.forEach((item) => {
						that.squares.forEach((it) => {
							if (it[0] == item[0] && item[1] > it[1]) {
								// x 轴坐标相同 且底部的y坐标比x轴大 说明 有交集
								arr.push(it[0])
							}
						})
					})
					that.intersectX = [...new Set(arr)] // 先去重复
				}
				// 找到下落物体的最大的Y
				findSeqaresMaxY() {
					that.squares.forEach((it) => {
						if (it[1] > that.squaresMaxY) {
							that.squaresMaxY = it[1]
						}
					})
				}
				// stopMove
				stopMove() {
					that.calculateIntersectX() //计算交叉范围

					let flag = false
					// 没有交范围
					if (!that.intersectX.length) {
						// 坐标中存在一个到达底部即停止
						that.squares.forEach((item) => {
							if (item[1] === that.MAP_HEGHT - 1) {
								flag = true
							}
						})
					} else {
						// 有交叉范围
						// 计算交叉范围内的x 相等情况下 ，y的差===1
						that.squares.forEach((it) => {
							that.bottomSquares.forEach((item) => {
								if (that.intersectX.includes(item[0])) {
									// 表示碰撞
									if (item[0] === it[0] && item[1] - it[1] === 1) {
										console.log('碰撞')
										flag = true
									}
								}
							})
						})
					}
					if (flag) {
						if (that.gamOver()) {
							that.clearTimer()
							alert('游戏结束')
							that.score = 0
							that.scoreEl.innerHTML = `得分为：${that.score}`
						} else {
							that.clearTimer()
							that.createdTarget()
						}
					}
				}
				clearTimer() {
					if (that.timer) clearInterval(that.timer)
					// 循环看是否需要消除一行
					that.bottomSquares = [...that.bottomSquares, ...that.squares]
					that.removeRow()
				}
				// 消除一行方块
				removeRow() {
					that.squares.forEach((item) => {
						let count = 0

						that.bottomSquares.forEach((it) => {
							if (it[1] === item[1]) {
								count++
							}
						})

						if (count == 20) {
							for (let k = 0; k < that.bottomSquares.length; k++) {
								if (that.bottomSquares[k][1] === item[1]) {
									that.bottomSquares.splice(k, 1)
									k--
								}
							}
							// 当前 删除上方所有方块下移一格
							for (let k = 0; k < that.bottomSquares.length; k++) {
								if (that.bottomSquares[k][1] < item[1]) {
									that.bottomSquares[k][1]++
								}
							}
							that.score++
							that.scoreEl.innerHTML = `得分为：${that.score}`
						} else {
							count = 0
						}
					})
				}
				// 结束游戏
				gamOver() {
					return that.bottomSquares.some((item) => item[1] == 0)
				}
				// 逆时针旋转
				blokTurnRight(blokArr) {
					var rows = blokArr.length
					var column = blokArr[0].length
					var newBlokArr = new Array()

					for (var i = 0; i < column; i++) {
						var tempArr = new Array()
						for (var j = rows - 1; j >= 0; j--) {
							tempArr.push(blokArr[j][i])
						}
						newBlokArr.push(tempArr)
						tempArr = null
					}
					return newBlokArr
				}

				//随机数
				random(n, m) {
					return Math.floor(Math.random() * (m - n + 1) + n)
				}
			}
			new Square('#map', '.score')
		</script>
	</body>
</html>
<!-- 
 困难点： 
 1.碰撞 :
 实现思路：
 	+ 每一次的移动 计算出当前移动方块集合与底部方块集合的交集范围
	+ 从交叉范围中计算每一个方块与正在下落方块集合的Y轴差值 
	+ 判断差值集合 只要存在一个差值为1 即下落物体集合与底部物体集合发生了碰撞
	+ 关闭当前定时器，将当前下落物体集合添加进底部物体集合，重现渲染页面，开启下一轮  
 2. 边界判断
 实现思路：
 	+ 每一次移动 遍历当前下落物体集合，所有的集合中的方块的x坐标 x++ 或者 x-- ,y轴坐标++ （采取绝对定位所以利用top值做位移 向下是y++） 
 	+ 关键点：无论其中一个坐标超出x轴范围 for循环都不能停止循环, 在每一循环中判断 当前x的是否越界，如果越界 再次遍历当前数组将所有x做x++,相当于是撤销了之前的操作
 3. 旋转 
  + 暂时不研究旋转算法  
4. 消除一行
  + 只有下碰撞的情况下才可能出现消除一行
	实现思路：
		+ 碰撞后将下落物体集合放到底部物体集合后 再变量下落物体集合（双重遍历）
		+ 第一层为下落物体集合 第二层为 底部物体集合，能够在底部物体集合中找到 20（一行填满为20）个y轴坐标相同的元素即表示当前一行已经填满，执行删除，开启下一轮循环
 -->
